#ifndef RINGBUFFER_H
#define RINGBUFFER_H

#include <iostream>
#include "RingBufferPolicies.h"

template<
	class T, unsigned N,
	class InsertOverflowPolicy = InsertOverwritePolicy,
	template <class> class GetEmptyPolicy = GetDefaultPolicy> 
class RingBuffer
	:	InsertOverflowPolicy,
		GetEmptyPolicy<T>
{
public:
	RingBuffer()
		:	start(0),
			end(0),
			size(N + 1), // keep one slot open for full/empty buffer distinction
			data(new T[size]) 
	{
	}

	~RingBuffer()
	{
		if(data != 0)
		{
			delete[] data;
			data = 0;
		}
	}

	void insert(const T& element)
	{
		// when handleOverflow returns false, data should be inserted nevertheless
		if(isFull() && InsertOverflowPolicy::handleOverflow(start, N))
			return;

		data[end] = element;
		increaseCounter(end);
	}

	T get()
	{
		T toReturn;

		if(isEmpty() && GetEmptyPolicy<T>::handleEmpty(toReturn))
			return toReturn;

		toReturn = data[start];
		increaseCounter(start);
		return toReturn;	
	}

	void print()
	{
		unsigned current = start;
		unsigned index = 0;

		while(current != end)
		{
			std::cout << "Element " << index << ": " << data[current] << std::endl;
			++index;
			increaseCounter(current);
		}
	}

	bool isFull() const
	{
		return ((end + 1) % size) == start;
	}

	bool isEmpty() const
	{
		return start == end;
	}

private:
	void increaseCounter(unsigned& value)
	{
		value = (value + 1) % size;
	}

private:
	unsigned start; // index oldest element in buffer
	unsigned end; // index for element to insert
	const unsigned long size;
	T* data;
};

#endif